#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include "server.h"

/* ** BOARD *************************************************************************
     
   left  A  B  C  D  E  F  G  H  right
      -- -- -- -- -- -- -- -- -- -- 
     |                             | 0
         -- -- -- -- -- -- -- --         
     |  |  |  |  |  |  |  |  |  |  | 1    BLACK
         -- -- -- -- -- -- -- --
     |  |  |  |  |  |  |  |  |  |  | 2
         -- -- -- -- -- -- -- --
     |  |  |  |  |  |  |  |  |  |  | 3
         -- -- -- -- -- -- -- --
     |  |  |  |  |  |  |  |  |  |  | 4
         -- -- -- -- -- -- -- --
     |  |  |  |  |  |  |  |  |  |  | 5
         -- -- -- -- -- -- -- --
     |  |  |  |  |  |  |  |  |  |  | 6
         -- -- -- -- -- -- -- --
     |  |  |  |  |  |  |  |  |  |  | 7
         -- -- -- -- -- -- -- --
     |  |  |  |  |  |  |  |  |  |  | 8    WHITE
         -- -- -- -- -- -- -- --
     |                             | 9
      -- -- -- -- -- -- -- -- -- --

************************************************************************************/

#define BACKLOG 5 // how many pending connections queue will hold
#define PACKETSIZE 36 //Size of Packet

/** Global Variables  **********/
Move my_move;

int sockfd, player1_fd, player2_fd;  // listen on sock_fd, new connections on player1_fd, player2_fd
struct sockaddr_in clientAddr, serverAddr;
int yes=1;
int rv;
int serverSock;
int clientSock;
unsigned int clientLen;
unsigned short serverPort;

char player1[INET6_ADDRSTRLEN], player2[INET6_ADDRSTRLEN];

/** Global Variables END *******/

void DieWithError(char *string) {
	printf("%s\n", string);
	exit(1);
}

int main(int argc, char *argv[]) {

  char buf[PACKETSIZE];
  int connect = 0;

  setup_tcp_sockets(argv[1]);

  while (1) {
    printf("Wait for Player1\n");

    recv_packet(player1_fd, buf);

    memcpy(&my_move, buf, PACKETSIZE);

    if(my_move.type == CHECKMATE) {
      printf("GAME OVER! YOU LOSE!");
      break;
    }
      

    get_client_move();//takes player2 input and sends it to arduino client 
    
    }

    
  return 0;
}



void get_client_move() {

  char move_str[25];
  Square src;
  Square dest;
  Movetype type;
  char in;
  char *inString = (char*)malloc(32);;

  printf("YOUR TURN: Make selection\n");
  printf("1. Standard Move (No check)\n");
  printf("2. Check-causing Move\n");
  printf("3. Checkmate-causing Move\n");
  printf("4. En Passant Move\n");
  printf("5. Queenside Castling Move\n");
  printf("6. Kingside Castling Move\n");
  printf("7. Promotion Move\n");

  printf("\nEnter Move (1 thru 7)\n");
  
  fscanf(stdin, "%s", inString);
  sscanf(inString, "%c", &in);
  printf("read in: \"%c\"", in);
  switch (in) {

  case '1':
    printf("Enter Move as lowercase string (eg:b6-f2), then hit ENTER:\n");
    fscanf(stdin, "%s[25]", move_str);
    src = (Square){NOPIECE, NOCOLOR, ((int)move_str[0])%96, ((int)move_str[1])%48, false, false, false};
    dest = (Square){NOPIECE, NOCOLOR, ((int)move_str[3])%96, ((int)move_str[4])%48, false, false, false};
    my_move = (Move){src, dest, NEWMOVE}; 
    break;

  case '2':
    printf("Enter Move as lowercase string (eg:b6-f2), then hit ENTER:\n");
    //read(0, (void*)move_str, 6);
    fscanf(stdin, "%s[25]",move_str);
    src= (Square){NOPIECE, NOCOLOR, ((int)move_str[0])%96, ((int)move_str[1])%48, false, false, false};
    dest = (Square){NOPIECE, NOCOLOR, ((int)move_str[3])%96, ((int)move_str[4])%48, false, false, false};
    my_move = (Move){src, dest, CHECK};
    break;

  case '3':
    printf("Enter Move as lowercase string (eg:b6-f2), then hit ENTER:\n");
    //read(0, (void*)move_str, 6);
    fscanf(stdin, "%s[25]",move_str);
    src= (Square){NOPIECE, NOCOLOR, ((int)move_str[0])%96, ((int)move_str[1])%48, false, false, false};
    dest = (Square){NOPIECE, NOCOLOR, ((int)move_str[3])%96, ((int)move_str[4])%48, false, false, false};
    my_move = (Move){src, dest, CHECKMATE};
    break;

  case '4':  
    printf("Enter Move as lowercase string (eg:b6-f2), then hit ENTER:\n");
    read(0, (void*)move_str, 6);
    src= (Square){NOPIECE, NOCOLOR, ((int)move_str[0])%96, ((int)move_str[1])%48, false, false, false};
    dest = (Square){NOPIECE, NOCOLOR, ((int)move_str[3])%96, ((int)move_str[4])%48, false, false, false};
    my_move = (Move){src, dest, ENPASSANT};
    break;

  case '5':
    src= (Square){NOPIECE, NOCOLOR, 1, 1, false, false, false};
    dest = (Square){NOPIECE, NOCOLOR, 1, 4, false, false, false};
    my_move = (Move){src, dest, CASTLING};
    break;

  case '6':
    src= (Square){NOPIECE, NOCOLOR, 1, 8, false, false, false};
    dest = (Square){NOPIECE, NOCOLOR, 1, 6, false, false, false};
    my_move = (Move){src, dest, CASTLING};
    break;

  case '7':
    printf("Enter Move as lowercase string (eg:b6-f2), then hit ENTER:\n");
    read(0, (void*)move_str, 6);
    src= (Square){NOPIECE, NOCOLOR, ((int)move_str[0])%96, ((int)move_str[1])%48, false, false, false};
    dest = (Square){NOPIECE, NOCOLOR, ((int)move_str[3])%96, ((int)move_str[4])%48, false, false, false};
    my_move = (Move){src, dest, PROMOTION};
    break;
  }

  char buf[36];
  memcpy(buf, &my_move, 36);
  send_packet(player1_fd, buf);
}

int recv_packet(int file_desc, char *buf) {
	accept_client_connections();
  printf("Inside recv_packet()\n");
  
  int recvMsgSize;                    /* Size of received message */

  /* Receive message from client */
	if ((recvMsgSize = recv(clientSock, buf, PACKETSIZE, 0)) < 0)
      DieWithError("recv() failed");

  close(clientSock);    /* Close client socket */
  printf("Exiting recv_packet() %d bytes read\n", recvMsgSize);
  return recvMsgSize;
}

int send_packet(int file_desc, char *buf) {
  accept_client_connections();
  printf("Sending packet\n");

  if (send(clientSock, buf, PACKETSIZE, 0) < 0)
      DieWithError("send() failed");

	close(clientSock);
  printf("Done sending\n");
  return PACKETSIZE;
}
    

void setup_tcp_sockets(char *port_num) {
	/* Create socket for incoming connections */
	if ((serverSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
		DieWithError("socket() failed");

	/* Construct local address structure */
  memset(&serverAddr, 0, sizeof(serverAddr));   /* Zero out structure */
  serverAddr.sin_family = AF_INET;                /* Internet address family */
  serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
  serverAddr.sin_port = htons(atoi(port_num));      /* Local port */

 /* Bind to the local address */
  if (bind(serverSock, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0)
    DieWithError("bind() failed");

	/* Mark the socket so it will listen for incoming connections */
  if (listen(serverSock, 5) < 0)  //5 max pending connections
    DieWithError("listen() failed");
  
}

void accept_client_connections() {
	printf("server: waiting for connection...\n");
  /* Set the size of the in-out parameter */
        clientLen = sizeof(clientAddr);

        /* Wait for a client to connect */
        if ((clientSock = accept(serverSock, (struct sockaddr *) &clientAddr, 
                               &clientLen)) < 0)
            DieWithError("accept() failed");

        /* clntSock is connected to a client! */
    printf("server got connection from player1: %s\n", player1);


}

/*
 * HELPER Method: get sockaddr, IPv4 or IPv6:                                                                       
 */
void *get_in_addr(struct sockaddr *sa) {
  if (sa->sa_family == AF_INET)
    return &(((struct sockaddr_in*)sa)->sin_addr);

  return &(((struct sockaddr_in6*)sa)->sin6_addr);
  }

